﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using Lidgren.Network;
using Network;
using Network.Messages.LiveControl;
using Providers.Extensions;

namespace Providers.LiveControl.Server.ScreenHelper
{
    public class RectGridNode
    {
        private readonly int _dimesion = 0;
        private readonly RectGridNode[,] _rectGridsNodeChild = null;
        private Rectangle _rect;
        private int offsetX;
        private int offsetY;
        private uint _index;
        public static int Mtu = 250;

        private int _row;
        private int _col;
        public RectGridNode(Queue<int> dimensionsQueue,int row,int col)
        {
            Queue<int> tdimQueue = new Queue<int>(dimensionsQueue);
            _row = row;
            _col = col;
            if (tdimQueue.Count > 0)
            {
                _dimesion = tdimQueue.Dequeue();
                _rectGridsNodeChild = new RectGridNode[_dimesion, _dimesion];

                for (int i = 0; i < _dimesion; i++)
                {
                    for (int j = 0; j < _dimesion; j++)
                    {
                        _rectGridsNodeChild[i, j] = new RectGridNode(tdimQueue, row*_dimesion+i,col*_dimesion+j);
                    }
                }
            }

        }

        private bool HasChild => _dimesion != 0;

        private bool SendAll;
        public uint SetIndex(uint indexPre)
        {
            if (HasChild)
            {
                foreach (var rectGrid in _rectGridsNodeChild)
                {
                    indexPre = rectGrid.SetIndex(indexPre);
                }
                _index = indexPre++;
                return indexPre;
            }
            else
            {
                _index = indexPre++;
                return indexPre;
            }
        }
        #region 设置格网矩形
        bool _flagNewRect = true;
        
        public void SetRect(Rectangle tRectangle, int offsetx,int offsety)
        {
            offsetX = offsetx;
            offsetY = offsety;
            if (_rect == tRectangle)
                return;
            _rect = tRectangle;
            _flagNewRect = true;
            if (!HasChild)
                return;
            int width = _rect.Width;
            int height = _rect.Height;

            int dx = _rect.Width / _dimesion;
            int dy = _rect.Height / _dimesion;

            int[] widths = new int[_dimesion];
            int[] heights = new int[_dimesion];

            SpiltLine(widths, dx, width);
            SpiltLine(heights, dy, height);

            int currentX = _rect.X;
            for (int i = 0; i < widths.Length; i++)
            {
                int currentY = _rect.Y;
                for (int j = 0; j < heights.Length; j++)
                {
                    _rectGridsNodeChild[i, j].SetRect(
                        new Rectangle(currentX, currentY, widths[i], heights[j]),
                       offsetx,offsety);
                    currentY += heights[j];
                }
                currentX += widths[i];
            }

        }
        private void SpiltLine(int[] lines, int dx, int totalLength)
        {
            for (int i = 0; i < _dimesion - 1; i++)
            {
                lines[i] = dx;
            }
            lines[_dimesion - 1] = totalLength - dx * (_dimesion - 1);
        }
        #endregion

        #region 设置截屏

        public bool Changed { get; private set; } = false;

        public bool CompareChange(Bitmap oldScreen, Bitmap screen, Rectangle rectBounds)
        {
            Changed = false;
            _flagNewRect = false;
            SendAll = true;

            if (HasChild)
            {
                int changeCount = 0;
                foreach (var rectGrid in _rectGridsNodeChild)
                {
                    bool temp = rectGrid.CompareChange(oldScreen, screen, rectBounds);
                    if (!temp) continue;
                    Changed = true;
                    changeCount++;
                }
                SendAll = changeCount >= _dimesion - 1;
                return Changed;
            }
            else
            {
                if (oldScreen == null || Changed)
                {
                    return Changed = true;
                }

                for (int i = _rect.X; i < _rect.X + _rect.Width; i++)
                {
                    for (int j = _rect.Y; j < _rect.Y + _rect.Height; j++)
                    {
                        if (screen.GetPixel(i, j).ToArgb() != oldScreen.GetPixel(i, j).ToArgb())
                        {
                            return Changed = true;
                        }
                    }
                }
                SendAll = false;
                return Changed = false;
            }
        }

        

        public void CompareChange(object param)
        {
            Changed = _flagNewRect;

            _flagNewRect = false;

            if (HasChild)
            {
                foreach (var rectGrid in _rectGridsNodeChild)
                {
                    //ThreadPool.QueueUserWorkItem(rectGrid.CompareChange, param);
                    rectGrid.CompareChange(param);
                }
                foreach (var rectGridNode in _rectGridsNodeChild)
                {
                    Changed = Changed || rectGridNode.Changed;
                }
                return;
            }
            else
            {
                TempParam tparam = (TempParam) param;
                if (tparam.OldScreen == null || Changed)
                {
                    Changed = true;
                    return;
                }
                
                 tparam.Bools[_col,_row] = Changed = PixelGrabber.IsChanged(tparam.OldScreen, tparam.Screen, _rect,tparam.Scan);
                return;
            }
        }

     

        internal void SendBitmap(int sendFrameIndex, Bitmap screen, NetworkPeer network, List<ClientRequestParam> paramList)
        {
            foreach (var clientRequestParam in paramList)
            {
                if (!clientRequestParam.CheckSend(_rect, Changed))
                    continue;

                if (HasChild)
                {
                    foreach (var rectGrid in _rectGridsNodeChild)
                    {
                        rectGrid.SendBitmap(sendFrameIndex,screen ,network, paramList);
                    }
                }
                else
                {
                    Bitmap regionShot = null;
                    try
                    {
                        regionShot = screen.Clone(_rect, PixelFormat.Format16bppRgb565);
                    }
                    catch (OutOfMemoryException)
                    {
                        Trace.WriteLine("OutOfMemoryException");
                    }
                    var stream = new MemoryStream();
                    regionShot?.Save(stream, ImageFormat.Jpeg);

                    // 缩略图
                    Image thumbnail = regionShot.GetThumbnailImage(regionShot.Width / 4, regionShot.Height / 4,
                        () => false, IntPtr.Zero);
                    var thumbnailStream = new MemoryStream();
                    thumbnail.Save(thumbnailStream, ImageFormat.Png);
                    SendFullBitmapAtOnce(sendFrameIndex, thumbnailStream.ToArray(), _rect, network, clientRequestParam.Connection, NetDeliveryMethod.ReliableUnordered);

                    SendFullBitmapAtOnce(sendFrameIndex, stream.ToArray(), _rect, network, clientRequestParam.Connection, SendConfig.ScreenNetDeliveryMethod);
                    //SendFragmentedBitmap(stream.ToArray(), _rect, network, connection);

                }

            }
            Changed = false;

        }
     
        public bool SendEmpty(NetworkPeer network, NetConnection connection)
        {
            if (!Changed)
            {
                network.SendMessage(new ResponseEmptyScreenshotMessage(), NetDeliveryMethod.ReliableOrdered,
                    0, connection);
                return true;
            }
            return false;
        }
       
        private void SendFullBitmapAtOnce(int frameIndex, byte[] bitmapBytes, Rectangle region, NetworkPeer network, NetConnection connection = null, NetDeliveryMethod method = NetDeliveryMethod.ReliableSequenced)
        {
            // Send ResponseBeginScreenshotMessage
            var fullScreenshotMessage = new ResponseFullScreenshotMessage
            {
                Number = _index,
                Region = region,
                Image = bitmapBytes,
                SendFrameIndex = frameIndex
            };

            network.SendMessage(fullScreenshotMessage, method, 0, connection);
            
            Trace.WriteLine(
                $"Completed send of screenshot #{_index}, Size: {bitmapBytes.Length.ToKilobytes()} KB");
        }
        private void SendFragmentedBitmap(byte[] bitmapBytes, Rectangle region, NetworkPeer network, NetConnection connection = null)
        {
            // Send ResponseBeginScreenshotMessage
            var beginMessage = new ResponseBeginScreenshotMessage
            {
                Number = _index,
                Region = region,
                FinalLength = bitmapBytes.Length,
            };

            network.SendMessage(beginMessage, NetDeliveryMethod.ReliableOrdered, 0, connection);

            // Send ResponseScreenshotMessage

            // We don't want to send a 300 KB image - we want to make each packet Mtu bytes
            // int numFragments = (bitmapBytes.Length / Mtu) + 1;
            var numFragments = (int)Math.Floor(bitmapBytes.Length / (decimal)Mtu) + 1;

            for (var i = 0; i < numFragments; i++)
            {
                var message = new ResponseScreenshotMessage();

                byte[] regionFragmentBuffer = null;

                if (i != numFragments - 1 && i < numFragments)
                {
                    regionFragmentBuffer = new byte[Mtu];
                    Buffer.BlockCopy(bitmapBytes, i * Mtu, regionFragmentBuffer, 0, Mtu);
                }
                else if (i == numFragments - 1 || numFragments == 1)
                {
                    var bytesLeft = bitmapBytes.Length % Mtu;
                    regionFragmentBuffer = new byte[bytesLeft];
                    Buffer.BlockCopy(bitmapBytes, i * Mtu, regionFragmentBuffer, 0, bytesLeft);
                }
                else if (i == numFragments - 1)
                {
                    break;
                }

                if (regionFragmentBuffer == null)
                    Debugger.Break();

                message.Number = _index;
                message.Image = regionFragmentBuffer;
                message.SendIndex = i <= 0 ? 0 : i;
                network.SendMessage(message, NetDeliveryMethod.UnreliableSequenced, 0, connection);
                Trace.WriteLine(
                    $"Sent screenshot #{_index}, fragment #{i} of {numFragments} ({message.Image.Length.ToKilobytes()} KB).");
            }

            network.SendMessage(new ResponseEndScreenshotMessage { Number = _index }, NetDeliveryMethod.ReliableOrdered, connection);
            Trace.WriteLine(
                $"Completed send of screenshot #{_index}, Size: {bitmapBytes.Length.ToKilobytes()} KB");
        }
        #endregion
    }

   public struct TempParam
    {
       public int[] OldScreen;
       public int[] Screen;
       //public bool[] ChangeFlags;
       public int Scan;
       public bool[,] Bools;
    }
}
